#pragma once

#include "../../corelib/singleton.hpp"
#include "framework.hh"
#include "../../corelib/util/allocator.hh"
#include <cstdint>
#include <ctime>

// Search info.
struct SearchInfo {
    SearchInfo(const SearchInfo&) = delete;
    const SearchInfo& operator=(const SearchInfo&) = delete;
    // ctor
    SearchInfo();
    // rvalue copy
    SearchInfo(SearchInfo&& rht);
    SearchMethod method; // Method for searching result
    SearchType searchType; // The searching type
    std::uint64_t userData; // User data
};

// Cache operation type
enum class CacheOP : std::uint32_t {
    ADD = 1, // Add
    REMOVE, // Remove
};

// Entity searching cache
class EntitySearchCache {
    using EntityIDSet = UNORDERED_SET(std::uint64_t, EntitySearchCache_entityIDSet_);
    EntityIDSet entityIDSet_; // The entities
    EntityIDVector entityIDs_; // The entity vector
    std::size_t roundIndex_; // The index for round-robin
    std::uint32_t entityType_; // entity type of cache
    bool dirty_; // Dirty flag

public:
    EntitySearchCache(const EntitySearchCache&) = delete;
    const EntitySearchCache& operator=(const EntitySearchCache&) = delete;
    // ctor
    EntitySearchCache(std::uint32_t entityType);
    // rvalue copy
    EntitySearchCache(EntitySearchCache&& rht);
    // dtor
    ~EntitySearchCache();
    // Searching entity in cache
    // @param searchInfo search info.
    // @retval true
    // @retval false fail
    bool search(const SearchInfo& searchInfo);
    // Returns the count of entity
    std::size_t size();
    // Returns entity ID
    const EntityIDVector& getEntityIDs() const;

private:
    // Add new entity
    // @param entityID entity ID
    void add(std::uint64_t entityID);
    // Remove entity
    // @param entityID entity ID
    void remove(std::uint64_t entityID);
    // Search one entity by the way of round-robin
    // @retval true
    // @retval false fail
    bool searchRoundRobin(const SearchInfo& searchInfo);
    // Rebuilds entity ID array for dirty flag is 'true'
    void tryRebuildIDArray();

    friend class EntitySearchCacheManager;
};

// Entity cache manager
class EntitySearchCacheManager {
    std::uint32_t sequence_; // method sequence
    using SearchCallMap = UNORDERED_MAP(std::uint32_t, SearchInfo, EntitySearchCacheManager_searchCallMap_);
    SearchCallMap searchCallMap_; // search call map
    using CacheMap = UNORDERED_MAP(std::uint32_t, EntitySearchCache, EntitySearchCacheManager_cacheMap_);
    CacheMap cacheMap_; // cache map
    using SequenceVector = std::vector<std::uint32_t>;
    using TypeSequenceMap = UNORDERED_MAP(std::uint32_t, SequenceVector, EntitySearchCacheManager_pendingSearches_);
    TypeSequenceMap pendingSearches_; // The pending searching
    using SearchTypeSet = UNORDERED_SET(std::uint32_t, EntitySearchCacheManager_searchTypeSet_);
    SearchTypeSet searchTypeSet_; // Search type

public:
    // ctor
    EntitySearchCacheManager();
    // Searchs entity by type
    // @param entityType entity type
    // @param searchType The searching type
    // @param method search result callback
    // @param timeout The timeout for first searching
    // @param userData user data
    // @retval true
    // @retval false fail
    bool search(std::uint32_t entityType, SearchType searchType, SearchMethod method, std::time_t timeout, std::uint64_t userData);
    // Start first search
    // @param entityType entity type
    // @param searchType The searching type
    // @param timeout The timeout for first searching
    // @retval true
    // @retval false fail
    bool startFirstSearch(std::uint32_t entityType, SearchType searchType, std::time_t timeout);
    // Updates the cache
    // @param entityType entity type
    // @param entityIDs entities need to update
    // @param cacheOP cache operation type
    void update(std::uint32_t entityType, const EntityIDVector& entityIDs, CacheOP cacheOP);
    // Research all
    void research();
    // Updating pending search
    bool updatePendingSearch(std::uint32_t entityType);
    // Returns all entity ID with type entityType
    // @param entityType entity type
    const EntityIDVector& getEntityIDs(std::uint32_t entityType);
};

#define EntitySearchCacheManagerRef (*kratos::corelib::Singleton<EntitySearchCacheManager>::instance())
#define EntitySearchSingleton kratos::corelib::Singleton<EntitySearchCacheManager>
